<template>
  <div
    class="flex"
    v-click-outside="onClickOutside"
  >
    <!-- 自定义选择弹出框 -->
    <el-popover
      popper-class="t-custom-select-popover"
      ref="popoverRef"
      :visible="visible"
      :virtual-ref="virtualRef"
      trigger="click"
      virtual-triggering
      placement="bottom"
      :width="width"
    >
      <!-- 搜索输入框 -->
      <div
        class="input-wrap"
        v-if="filterable"
      >
        <el-input
          size="default"
          class="popover-filter-input mb6"
          v-model="filterText"
          @input="handleFilter"
          :placeholder="filterPlaceholder"
        />
      </div>
      <!-- 选项列表 -->
      <el-scrollbar
        ref="scrollbarRef"
        style="height: 200px"
      >
        <template
          v-for="option in optionList"
          :key="option[optionProps.value]"
        >
          <div
            class="el-dropdown-menu__item"
            :class="[option[optionProps.value] === targetItem[targetKey] ? 'el-dropdown-menu__item__active' : '']"
            v-if="!filterOptionsKey.includes(option[optionProps.value])"
            @click="handleOptionClick(option[optionProps.value])"
          >
            {{ option[optionProps.label] }}
          </div>
        </template>
      </el-scrollbar>
    </el-popover>
  </div>
</template>

<script lang="ts" setup>
import type { Ref } from "vue";
import { nextTick, onMounted, onUnmounted, ref, watch } from "vue";
import { cloneDeep } from "lodash-es";
import type { ElPopover, ScrollbarInstance } from "element-plus";
import { ClickOutside as vClickOutside } from "element-plus";

// 封装这个组件的目的 逻辑里面会存在大量的 select 如果有 1000 个题目 配置逻辑多的话
// 将会存在上万个或者更多的 dom
// 封装这个组件 1000 个题目就只渲染 1000 个 重复用这个组件去显示

// 定义接口
interface OptionProps {
  label: string;
  value: string;
}

interface Option {
  [key: string]: any;
}

interface TargetItem {
  [key: string]: any;
}

// 组件状态
const visible: Ref<boolean> = ref(false);
const virtualRef: Ref<HTMLElement | null> = ref(null);
const width: Ref<string> = ref("200px");
const popoverRef: Ref<InstanceType<typeof ElPopover> | null> = ref(null);
const optionList: Ref<Option[]> = ref([]);
const filterOptionsKey: Ref<string[]> = ref([]);
const filterText: Ref<string> = ref("");
const targetItem: Ref<TargetItem> = ref({});
const targetKey: Ref<string> = ref("");

// 组件属性定义
const props = withDefaults(
  defineProps<{
    options: Option[];
    optionProps?: OptionProps;
    filterable?: boolean;
    filterPlaceholder?: string;
  }>(),
  {
    optionProps: () => ({
      label: "label",
      value: "value"
    }),
    filterable: false,
    filterPlaceholder: "请输入搜索内容"
  }
);

// 事件定义
const emit = defineEmits<{
  (e: "change", value: any): void;
}>();

/**
 * 监听选项列表变化
 */
watch(
  () => props.options,
  (newOptions: Option[]) => {
    if (Array.isArray(newOptions)) {
      optionList.value = cloneDeep(newOptions);
    }
  },
  {
    immediate: true,
    deep: true
  }
);

/**
 * 处理搜索过滤
 * @param {string} val - 搜索关键词
 */
const handleFilter = (val: string): void => {
  filterText.value = val;
  if (val) {
    optionList.value = optionList.value.filter(item => item[props.optionProps.label]?.toLowerCase().includes(val.toLowerCase()));
  } else {
    optionList.value = cloneDeep(props.options);
  }
};

/**
 * 处理选项点击
 * @param {string|number} val - 选中的值
 */
const handleOptionClick = (val: string | number): void => {
  handleHidePopver();
  if (targetItem.value && targetKey.value) {
    targetItem.value[targetKey.value] = val;
    emit("change", val);
  }
};

/**
 * 处理点击外部区域
 * @param {Event} event - 点击事件
 */
const onClickOutside = (event: Event): void => {
  const target = event.target as HTMLElement;
  if (target.classList.contains("popover-filter-input") || target.classList.contains("el-input__inner")) {
    return;
  }
  handleHidePopver();
};

const scrollbarRef = ref<ScrollbarInstance | null>(null);

/**
 * 处理点击事件
 * @param {Event} event - 点击事件
 * @param {Object} item - 目标数据对象
 * @param {string} key - 目标数据键名
 * @param {Array} fKeys - 过滤键名数组
 */
const handleClick = (event: Event, item: TargetItem, key: string, fKeys?: string[]): void => {
  if (!event || !item || !key) {
    console.warn("handleClick: Missing required parameters");
    return;
  }

  let targetDom = event.target as HTMLElement;
  if (targetDom.classList.contains("el-select__placeholder")) {
    targetDom = targetDom.parentNode as HTMLElement;
  }

  virtualRef.value = targetDom;
  targetDom.style.boxShadow = "0 0 0 1px var(--el-color-primary) inset";
  targetDom.classList.remove("logic-custom-select");
  targetDom.classList.add("logic-custom-select-expand");
  width.value = `${targetDom.offsetWidth}px`;
  visible.value = true;
  targetItem.value = item;
  targetKey.value = key;
  filterOptionsKey.value = fKeys || [];

  // 滚动到选中项
  const activeItem = scrollbarRef.value!.$el.querySelector(".el-dropdown-menu__item__active");
  if (activeItem) {
    scrollToSelected();
  } else {
    nextTick(() => {
      scrollToSelected();
    });
  }
};

const scrollToSelected = () => {
  const scrollbar = scrollbarRef.value!.$el;
  if (scrollbar) {
    const activeItem = scrollbar.querySelector(".el-dropdown-menu__item__active");
    if (activeItem) {
      activeItem.scrollIntoView({ block: "center", behavior: "smooth" });
    }
  }
};

/**
 * 隐藏题目下拉
 */
const handleHidePopver = (): void => {
  if (virtualRef.value) {
    virtualRef.value.style.boxShadow = "none";
    virtualRef.value.classList.add("logic-custom-select");
    virtualRef.value.classList.remove("logic-custom-select-expand");
  }
  visible.value = false;
};

// 滚动事件处理
let scrollableDiv: HTMLElement | null = null;

onMounted(() => {
  // 滚动时候隐藏下拉列表
  scrollableDiv = document.querySelector(".logic-scrollbar-wrap");
  if (scrollableDiv) {
    scrollableDiv.addEventListener("scroll", handleHidePopver);
  }
});

onUnmounted(() => {
  if (scrollableDiv) {
    scrollableDiv.removeEventListener("scroll", handleHidePopver);
  }
  handleHidePopver(); // 确保组件卸载时清理状态
});

// 暴露方法
defineExpose({
  handleClick
});
</script>

<style lang="scss" scoped>
.el-dropdown-menu__item {
  cursor: pointer;
  transition: all 0.3s ease;

  &:hover {
    background-color: var(--el-bg-color-page);
  }
}

.el-dropdown-menu__item__active {
  color: var(--el-color-primary);
  font-weight: 500;
}
</style>

<style lang="scss">
.t-custom-select-popover {
  --el-popover-padding: 2px;

  .el-scrollbar__wrap {
    max-height: 200px;
  }
}
</style>
